# -*- coding: latin-1 -*-
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (c) 2010 by Derek Anderson
#

import collections, datetime, json, os, random, re, sqlite3, subprocess, sys
import thread, threading, time, traceback, uuid

import pygtk
pygtk.require("2.0")
import gtk
import gtk.glade
import gtksourceview2
import gobject, gnome
import pango

import sqlparse


PROGRAM_NAME = 'DeSQL'
PROGRAM_VERSION = '0.2'
NAMESPACE_DESQL = uuid.UUID('efd06ac7-1cb0-40c6-adf3-eda43fbc1f9d')
DND_RESULTS = [("results/x-application-desql",0,-1)]
RUN_FROM_DIR = os.path.dirname(sys.argv[0])
print 'RUN_FROM_DIR', RUN_FROM_DIR
DB_PATH = os.path.join(os.path.expanduser('~'),'.desql.db')
PK_KEY = '⚷'
FK_KEY = '∊'


_sqlite3_conn_cache = {}


def get_log_conn():
  return get_sqlite3_conn(DB_PATH)


def get_sqlite3_conn(path):
  tid = thread.get_ident()
  conn = _sqlite3_conn_cache.get((path,tid))
  if not conn:
    _sqlite3_conn_cache[(path,tid)] = conn = sqlite3.connect(path)
  return conn


def init_db():
  log_conn = get_log_conn()
  #log_conn = sqlite3.connect( os.path.join(os.path.expanduser('~'),'.desql.db') )
  cur = log_conn.cursor()
  cur.execute('create table if not exists sql_log(conn_id VARCHAR, session_id VARCHAR, tran_id VARCHAR, '+
              'sql_id VARCHAR, sql VARCHAR, start_time FLOAT, exec_time FLOAT, hidden BOOLEAN)')
  cur.execute('create table if not exists sql_log_cache(sql_id VARCHAR, row_count INTEGER, headers VARCHAR, '+
              'data VARCHAR)')
  cur.execute('create table if not exists metadata(object_id VARCHAR, attribute VARCHAR, value VARCHAR)')
  cur.execute('create table if not exists false_keys(conn_id VARCHAR, schema_name VARCHAR, table_name VARCHAR, column_name VARCHAR, '+
              'from_conn_id VARCHAR, from_schema_name VARCHAR, from_table_name VARCHAR, from_column_name VARCHAR)')
  cur.close()
  log_conn.commit()


def prompt_for_text(markup, default=None, multiline=False):
  dialog = gtk.MessageDialog(flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_OK_CANCEL)
  dialog.set_markup(markup)
  if multiline:
    textview = gtk.TextView()
    scrollwindow = gtk.ScrolledWindow()
    scrollwindow.add(textview)
    scrollwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
    dialog.vbox.pack_end(scrollwindow, True, True, 0)
    if default:
      textview.get_buffer().set_text(default)
  else:
    entry = gtk.Entry()
    if default:
      entry.set_text(default)
    def f(entry, dialog, response):
      dialog.response(response)
    entry.connect("activate", f, dialog, gtk.RESPONSE_OK)
    dialog.vbox.pack_end(entry, True, True, 0)
  dialog.show_all()
  if dialog.run()==-5:
    if multiline:
      buf = textview.get_buffer()
      text = buf.get_text(buf.get_start_iter(),buf.get_end_iter())
    else:
      text = entry.get_text()
    dialog.destroy()
    return text
  else:
    dialog.destroy()


METADATA_CACHE = {}
METADATA_TO_WRITE = {}



def set_metadata(uid,attribute,value):
  METADATA_CACHE[(uid,attribute)] = value
  METADATA_TO_WRITE[(uid,attribute)] = value


def _metadata_write_thread_run():
  while True:
    time.sleep(60)
    log_conn = get_log_conn()
    cur = log_conn.cursor()
    for k,value in METADATA_TO_WRITE.items():
      uid,attribute = k
      value_json = json.dumps(value)
      params = {'object_id':str(uid), 'attribute':attribute, 'value':value_json}
      cur.execute('delete from metadata where object_id=:object_id and attribute=:attribute',params)
      cur.execute('insert into metadata (object_id, attribute, value) values (:object_id, :attribute, :value);',params)
      del METADATA_TO_WRITE[(uid,attribute)]
      print 'wrote', (uid,attribute), value_json if len(value_json)<256 else '(a big value)'
    cur.close()
    log_conn.commit()

METADATA_WRITE_THREAD = threading.Thread(target=_metadata_write_thread_run)
METADATA_WRITE_THREAD.daemon = True
METADATA_WRITE_THREAD.start()


def get_metadata(uid,attribute,default=None):
  if (uid,attribute) in METADATA_CACHE:
    return METADATA_CACHE[(uid,attribute)]
#  old_key = uid
#  if not isinstance(uid,uuid.UUID):
#    uid = uuid.uuid5( NAMESPACE_DESQL, str(uid) )
  log_conn = get_log_conn()
  cur = log_conn.cursor()
  params = {'object_id':str(uid), 'attribute':attribute}
  cur.execute('select value from metadata where object_id=:object_id and attribute=:attribute',params)
  value = None
  for row in cur:
    value = json.loads(row[0])
    cur.close()
    #print 'found uid,attribute,value:', old_key,attribute,value
    METADATA_CACHE[(uid,attribute)] = value
    return value
  cur.close()
  #print 'did not find uid,attribute:', old_key,attribute
  return default


def pluralize(i):
  if i==1 or i==-1: return ''
  else: return 's'


def get_parent_notebook_and_page(o):
  notebook = o
  while not isinstance(notebook,gtk.Notebook):
    o = notebook
    notebook = notebook.get_parent()
  for i in range(notebook.get_n_pages()):
    if notebook.get_nth_page(i)==o or notebook.get_tab_label(notebook.get_nth_page(i))==o:
      return notebook, i
  return notebook, -1


def find_label_text(w):
  if isinstance(w,gtk.Label):
    return w.get_text()
  for c in w.get_children():
    t = find_label_text(c)
    if t!=None:
      return t

# allow tabs to be dragged off into their own window
def result_drag_failed_gen_window(label, drag_context, result, page):
  window = gtk.Window(gtk.WINDOW_TOPLEVEL)
  window.set_default_size(480, 240)
  window.connect("destroy", lambda w: window.destroy())
  notebook = gtk.Notebook()
  notebook.set_tab_pos(gtk.POS_BOTTOM)
  def drop_cb(notebook, context, x, y, time):
    src_notebook, page_num = get_parent_notebook_and_page( context.get_source_widget() )
    move_notebook_page_to_notebook( src_notebook, page_num, notebook )
    return True
  notebook.connect('drag_drop', drop_cb)
  notebook.drag_dest_set(gtk.DEST_DEFAULT_ALL, DND_RESULTS, gtk.gdk.ACTION_MOVE)
  window.add(notebook)
  window.show_all()
  src_notebook, page_num = get_parent_notebook_and_page(page)
  move_notebook_page_to_notebook( src_notebook, page_num, notebook )
  return True


def move_notebook_page_to_notebook( src_notebook, page_num, dest_notebook ):
  page = src_notebook.get_nth_page(page_num)
  label = src_notebook.get_tab_label(page)
  label_text = find_label_text(label)
  page.reparent(dest_notebook)
  event_box = gtk.EventBox()
  event_box.add(gtk.Label(label_text))
  event_box.drag_source_set(gtk.gdk.BUTTON1_MASK, DND_RESULTS, gtk.gdk.ACTION_MOVE)
  event_box.connect("drag-failed", result_drag_failed_gen_window, page)
  close = gtk.Button()
  close.set_image( gtk.image_new_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU) )
  close.set_relief(gtk.RELIEF_NONE)
  close.set_border_width(0)
  close.set_tooltip_text('close this tab')
  close.connect('clicked',lambda x: dest_notebook.remove_page(dest_notebook.page_num(page)) )
  box = gtk.HBox()
  box.add(event_box)
  box.add(close)
  box.show_all()
  dest_notebook.set_tab_label(page,tab_label=box)
  
  
def hidden_color(hidden):
  if hidden: return 'gray'
  else: return 'black'


def load_data_to_treeview(headers, data, treeview, max_rows=10000):
  treestore = gtk.TreeStore(*[str for x in headers])
  for i in range(len(headers)):
    treestore.set_sort_func(i,sort_func)
  if len(data)>max_rows:
    displayed_data = data[:max_rows]
  else:
    displayed_data = data
  for row in displayed_data:
    treestore.append(None,row)
  treeview.set_model(treestore)
  return len(displayed_data)==len(data)


def get_count_headers_data_from_cur(cur):
  if cur.description:
    headers = []
    data = []
    for col in cur.description:
      headers.append(col[0])
    for row in cur:
      data.append([_sql_to_unicode(x) for x in row])
    return cur.rowcount, headers, data


def _sql_to_unicode(x):
  if x is None:
    return u'\u2205'
  if isinstance(x,(int,long,datetime.datetime)):
    return str(x)
  return x.decode('latin-1')


def pango_escape(text,menu=True):
  if not isinstance(text,basestring):
    text = str(text)
  text = text.replace('&','&amp;').replace('<','&lt;').replace('>','&gt;')
  if menu: text = text.replace('_','__')
  return text
  
def pango_descape(text):
  if not isinstance(text,basestring):
    text = str(text)
  return text.replace('&lt;','<').replace('&gt;','>').replace('&amp;','&').replace('__','_')

def menu_escape(text):
  if not isinstance(text,basestring):
    text = str(text)
  text = text.replace('_','__')
  return text
  
def menu_descape(text):
  if not isinstance(text,basestring):
    text = str(text)
  return text.replace('__','_')


def get_conn_info(conn_id):
  log_conn = get_log_conn()
  cur = log_conn.cursor()
  cur.execute('select db_type, server, port, username, password, schema, ssh_tunnel, count from connections where conn_id=?', (conn_id,))
  if not cur.rowcount: return None,None
  info = None
  for row in cur:
    info = {
      'db_type': row[0],
      'server': row[1],
      'username': row[3],
      'schema': row[5],
      'ssh_tunnel': row[6],
    }
    try: info['port'] = int(row[2])
    except: info['port'] = 0
    try: info['count'] = int(row[7])
    except: info['count'] = 0
    break
  cur.close()
  return info
  

SSH_TUNNEL_CACHE = {} # (dest_server,dest_port) => (actual_server,actual_port,process)
def get_ssh_tunnel(server, port, ssh_tunnel):
  if (server,port) not in SSH_TUNNEL_CACHE:
    actual_server = 'localhost'
    actual_port = random.randint(10000,20000)
    dest_port = port
    process = subprocess.Popen(["/usr/bin/ssh", '-fN', '-L', '%i:%s:%i'%(actual_port,server,dest_port), ssh_tunnel])
    print 'started process',process.pid
    process.wait()
    #time.sleep(5)
    SSH_TUNNEL_CACHE[(server,port)] = actual_server, actual_port, process
  
  actual_server, actual_port, process = SSH_TUNNEL_CACHE[(server,port)]
  return actual_server, actual_port


def create_conn(conn_id=None, db_type=None, server=None, port=None, username=None, password=None, schema=None, 
    ssh_tunnel=None, count=0, callback=None, callback_error=None):
  print 'conn_id', conn_id
  print conn_id, db_type, server, port, username, password, schema, ssh_tunnel, count, callback
  def doit(conn_id, db_type, server, port, username, password, schema, ssh_tunnel, count, callback):
    log_conn = get_log_conn()
    if conn_id:
      cur = log_conn.cursor()
      cur.execute('select db_type, server, port, username, password, schema, ssh_tunnel, count from connections where conn_id=?', (conn_id,))
      if not cur.rowcount: return None,None
      for row in cur:
        db_type = row[0]
        server = row[1]
        try: port = int(row[2])
        except: port = 0
        username = row[3]
        password = row[4]
        schema = row[5]
        ssh_tunnel = row[6]
        count = row[7]
        #schema = row['schema']
        break
      cur.close()
    conn = None

    if ssh_tunnel:
      if not port:
        if db_type=='MySQL':
          port = 3306
        if db_type=='SQL Server':
          port = 1433
      actual_server, actual_port = get_ssh_tunnel(server, port, ssh_tunnel)
    else:
      actual_server = server
      actual_port = port
    
    try:
      if db_type=='MySQL':
        import MySQLdb
        if actual_port:
          conn = MySQLdb.connect(host=actual_server, port=actual_port, user=username, passwd=password, db=schema)
        else:
          conn = MySQLdb.connect(host=actual_server, user=username, passwd=password, db=schema)
      if db_type=='SQLite3':
        conn = get_sqlite3_conn(server)
      if db_type=='SQL Server':
        import pymssql
        if schema:
          if actual_port:
            conn = pymssql.connect(host=actual_server+':'+str(actual_port), user=username, password=password, database=schema)
          else:
            conn = pymssql.connect(host=actual_server, user=username, password=password, database=schema)
        else:
          if actual_port:
            conn = pymssql.connect(host=actual_server+':'+str(actual_port), user=username, password=password)
          else:
            conn = pymssql.connect(host=actual_server, user=username, password=password)
        # force auto-commit=False (pymssql should be doing this)
        conn.autocommit(True)
        tmp_cur = conn.cursor()
        tmp_cur.execute('SET IMPLICIT_TRANSACTIONS ON;')
        tmp_cur.close()
    except Exception as e:
      traceback.print_exc()
      print e
      if callback_error:
        callback_error(str(e))
        
    if conn:
      if not conn_id:
        conn_id = str(uuid.uuid4())
        print 'new conn_id', conn_id
        cur = log_conn.cursor()
        cur.execute('select * from connections where db_type=? and server=? and port=? and username=? and password=? and schema=? and ssh_tunnel=?', (db_type,server,port,username,password,schema,ssh_tunnel))
        if cur.rowcount<=0:
          cur.execute('insert into connections (conn_id,db_type,server,port,username,password,schema,ssh_tunnel,count) values (?,?,?,?,?,?,?,?,?)', (conn_id,db_type,server,port,username,password,schema,ssh_tunnel,count))
          print 'saving conn', cur.rowcount
        else:
          for row in cur:
            conn_id = row[0]
            break
        cur.close()
        log_conn.commit()
      print 'creating connection', conn_id
      cur = log_conn.cursor()
      cur.execute('update connections set count=count+1 where conn_id=?', (conn_id,))
      cur.close()
      log_conn.commit()
      if callback: callback(conn_id, conn)
      return conn_id, conn
    return None, None

  if callback:
    t = threading.Thread(target=doit, args=(conn_id, db_type, server, port, username, password, schema, ssh_tunnel, count, callback))
    t.start()
  else:
    return doit(conn_id, db_type, server, port, username, password, schema, ssh_tunnel, count, callback)


def sort_func(m,a,b):
  return cmp(natural(m.get_value(a,m.get_sort_column_id()[0])),natural(m.get_value(b,m.get_sort_column_id()[0])))
  

def natural(key):
  if isinstance(key, basestring):
    if isinstance(key, unicode):
      key=unicodedata.normalize('NFKD', key.lower()).encode('ascii', 'ignore')
    else:
      key=key.lower()
    return [int(n) if n else s for n,s in re.findall(r'(\d+)|(\D+)', key)]
  else:
    return key


def scroll_to_selection(treeview):
  model, paths = treeview.get_selection().get_selected_rows()
  if paths:
    treeview.scroll_to_cell(paths[0])


def treeview_to_csv(treeview, only_selected=True, delim=','):
  if only_selected:
    m, selected_rows = treeview.get_selection().get_selected_rows()
  else:
    m = treeview.get_model()
    selected_rows = [ row.path for row in m ]
  cols = treeview.get_columns()
  visible_headers = [ col.get_title() for col in cols if col.get_visible() ]
  s = [delim.join(visible_headers)]
  for row in selected_rows:
    r = []
    itr = m.get_iter(row)
    for i in range(m.get_n_columns()):
      if not cols[i].get_visible(): continue
      v = m.get_value(itr,i)
      if v and '"' in v:
        v = '"%s"' % (v.replace('"',r'\"'))
      if v: r.append(v)
      else: r.append('')
    s.append(delim.join(r))
  return '\n'.join(s)

  
RE_NUMERIC = re.compile(r'^\d+[.]?\d*$')


def treeview_to_insert(treeview, table_name, only_selected=True):
  def func(table_name, headers, values):
    sh = ','.join(headers)
    sv = ','.join([sql_param_quote(v) for v in values])
    return 'insert into %s (%s) values (%s);' % (table_name, sh, sv)
  return treeview_to_func(treeview, table_name, func, only_selected)
  
  
def treeview_to_update(treeview, table_name, only_selected=True):
  def func(table_name, headers, values):
    s = build_where_clause(headers, values)
    return 'update %s set ? where %s;' % (table_name, s)
  return treeview_to_func(treeview, table_name, func, only_selected)


def build_where_clause(headers, values, joiner=' and '):
  s = [ '%s is null'%h if v==u'\u2205' else '%s=%s'%(h,sql_param_quote(v)) for h,v in zip(headers, values) ]
  return joiner.join(s)
  
  
def treeview_to_delete(treeview, table_name, only_selected=True):
  def func(table_name, headers, values):
    s = build_where_clause(headers, values)
    return 'delete from %s where %s;' % (table_name, s)
  return treeview_to_func(treeview, table_name, func, only_selected)
  
  
def treeview_to_func(treeview, table_name, func, only_selected=True):
  if only_selected:
    m, selected_rows = treeview.get_selection().get_selected_rows()
  else:
    m = treeview.get_model()
    selected_rows = [ row.path for row in m ]
  cols = treeview.get_columns()
  visible_headers = [ pango_descape(col.get_title()) for col in cols if col.get_visible() ]
  s = []
  for row in selected_rows:
    r = []
    itr = m.get_iter(row)
    for i in range(m.get_n_columns()):
      if not cols[i].get_visible(): continue
      v = m.get_value(itr,i)
      r.append(v)
    s.append(func(table_name, visible_headers, r))
  return '\n'.join(s)


def sql_param_quote(v):
  if v==u'\u2205': return 'null'
  if v and "'" in v:
    v = v.replace("'",r"\'")
  if RE_NUMERIC.match(v): return v
  else: return"'%s'" % v
  
  

def get_common_queries(conn_id, max_count=10):
  re_spacing = re.compile(r'\s+')
  re_numbers = re.compile(r'[0-9]*[.]?[0-9]+')
  re_strings = re.compile(r"'[^']+'")
  re_top = re.compile(r'(?<=[Tt][Oo][Pp] )[?]')
  log_conn = get_log_conn()
  cur = log_conn.cursor()
  cur.execute('select sql from sql_log where conn_id=?', (conn_id,))
  sql_counts = collections.defaultdict(int)
  for row in cur:
    s = row[0]
    s = re_spacing.sub(' ',s)
    s = re_strings.sub('?',s)
    s = re_numbers.sub('?',s)
    s = re_top.sub('1000',s)
    sql_counts[s] += 1
  common_sql = [ (v,k) for k,v in sql_counts.iteritems() if v>1 ]
  common_sql.sort()
  common_sql.reverse()
  return [x[1] for x in common_sql][:max_count]
    
  
def get_clipboard():
  return gtk.Clipboard(gtk.gdk.display_manager_get().get_default_display(), "CLIPBOARD")  


def get_tables_from_sql(sql, schema=None):
  parse = sqlparse.parse(sql)
  seen_tables = set()
  if parse:
    print parse[-1]
    stmt = parse[-1]
    seen_tables |= get_tables_from_stmt(stmt)
  print 'seen_tables', seen_tables
  return list(seen_tables)


def get_tables_from_stmt(stmt, schema=None):
  seen_tables = set()
  from_token = stmt.token_next_match(0, sqlparse.tokens.Token.Keyword, 'from')
  tables = stmt.token_next(from_token)
  print 'tables', tables, tables.__class__
  if isinstance(tables,sqlparse.sql.Identifier):
    table_list = [tables]
  if isinstance(tables,sqlparse.sql.IdentifierList):
    table_list = tables.tokens
  for table in table_list:
    print 'table', table, table.__class__
    if isinstance(table,sqlparse.sql.Identifier):
      if len(table.tokens)==3:
        seen_tables.add( (str(table.tokens[0]),str(table.tokens[2])) )
      else:
        seen_tables.add( (schema,str(table.tokens[0])) )
  print 'seen_tablesstmt', seen_tables
  return seen_tables
  

def get_sql_associated_with_treeview(treeview):
  tmp = treeview.get_parent().get_parent().get_children()
  expander = [x for x in tmp if isinstance(x,gtk.Expander)][0]
  print expander
  tmp = expander.get_children()
  sourceview = [x for x in tmp if isinstance(x,gtksourceview2.View)][0]
  buf = sourceview.get_buffer()
  sql = buf.get_text(buf.get_start_iter(),buf.get_end_iter())
  return sql
  
  
def async_file_write(fn,s):
  def run():
    with open(fn,'w') as f:
      f.write(s)
    print 'wrote',fn
  t = threading.Thread(target=run)
  t.start()
  

def show_simple_info_dialog(s):
  dialog = gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_OK, message_format=None)
  dialog.set_markup(s)
  dialog.connect('delete-event', lambda x,y,d: d.destroy(), dialog )
  dialog.connect('response', lambda x,y,d: d.destroy(), dialog )
  dialog.show_all()

def prompt_yes_no(s):
  dialog = gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_QUESTION, 
                             buttons=gtk.BUTTONS_YES_NO, message_format=None)
  dialog.set_markup(s)
  dialog.connect('delete-event', lambda x,y,d: d.destroy(), dialog)
  if dialog.run()==-8:
    dialog.destroy()
    return True
  dialog.destroy()
  return False

def check_and_prompt_for_vacuum():
  last_run = get_metadata(NAMESPACE_DESQL,'vacuum_last_run')
  size = os.path.getsize(DB_PATH) / (1024*1024)
  if not last_run: last_run = 0
  if size < 512 or time.time() - last_run < 1296000: 
    print 'logging db cleanup not needed'
    return
  if random.randint(0,8) != 2: 
    print 'logging db cleanup needed, but not today'
    return
  
  dialog = gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_QUESTION, 
                             buttons=gtk.BUTTONS_YES_NO, message_format=None)
  dialog.set_markup('''Your DeSQL logging database (containing historical information about SQL queries you've run) is '''+
                    '''currently %iMB.  Would you like to clear out old/large cached results?\n\n''' % (size,)+
                    '''Old/large meaning both older than 30 days and 256 rows returned...''')
  dialog.connect('delete-event', lambda x,y,d: d.destroy(), dialog)
  if dialog.run()==-8:
      dialog.destroy()
      dialog2 = gtk.MessageDialog(parent=None, flags=gtk.DIALOG_MODAL, type=gtk.MESSAGE_INFO, 
                             buttons=gtk.BUTTONS_NONE, message_format=None)
      dialog2.set_markup('''The cleanup of DeSQL's logging database is in progress.\n\n'''+
                         '''Please wait... (this is not cancellable)''')
      dialog2.connect('delete-event', lambda *_: '', dialog2)
      dialog2.vbox.get_children()[0].get_children()[1].get_children()[0].set_selectable(False)
      spinner = gtk.Spinner()
      dialog2.action_area.pack_end(spinner)
      spinner.start()
      spinner.show()
      def run():
        log_conn = get_log_conn()
        cur = log_conn.cursor()
        sql = '''delete from sql_log_cache where sql_id in (select sql_id from sql_log 
                 where start_time < %i) and row_count > %i;''' % (time.time()-2592000, 256)
        #print sql
        cur.execute(sql)
        cur.execute('vacuum;')
        cur.close()
        set_metadata(NAMESPACE_DESQL,'vacuum_last_run', time.time())
        with gtk.gdk.lock:
          dialog2.destroy()
      t = threading.Thread(target=run)
      t.start()
      dialog2.run()
        


